home *** CD-ROM | disk | FTP | other *** search
Text File | 1992-02-18 | 57.0 KB | 1,960 lines |
- Newsgroups: comp.sources.unix
- From: brnstnd@nyu.edu (Dan Bernstein)
- Subject: v25i130: Generalized interface to pseudo-tty devices, Part04/09
- Sender: unix-sources-moderator@pa.dec.com
- Approved: vixie@pa.dec.com
-
- Submitted-By: brnstnd@nyu.edu (Dan Bernstein)
- Posting-Number: Volume 25, Issue 130
- Archive-Name: pty4/part04
-
- #! /bin/sh
- # This is a shell archive. Remove anything before this line, then unpack
- # it by saving it into a file and typing "sh file". To overwrite existing
- # files, type "sh file -c". You can also feed this as standard input via
- # unshar, or by typing "sh <file", e.g.. If this archive is complete, you
- # will see the following message at the end:
- # "End of archive 4 (of 9)."
- # Contents: BLURB INSTALL.c NOTES SECURITY SYSCONF TESTS checkptys.c
- # env.c fmt.c scan.c
- # Wrapped by vixie@cognition.pa.dec.com on Wed Feb 19 13:35:04 1992
- PATH=/bin:/usr/bin:/usr/ucb ; export PATH
- if test -f 'BLURB' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'BLURB'\"
- else
- echo shar: Extracting \"'BLURB'\" \(4340 characters\)
- sed "s/^X//" >'BLURB' <<'END_OF_FILE'
- pty is meant as the sole interface between pseudo-terminals and the rest
- of the system. Rich Salz said of pty 3.0: ``This is the Ginsu knife (it
- slices, it dices, it never rusts) that Dan has been talking about in
- comp.unix.wizards/internals for some time now. It is a mind-blower.''
- But I just couldn't leave well enough alone, so here's pty 4.0, a vastly
- improved rewrite of the entire package. A taste of what it has to offer:
- X
- X* Improved security - pty 3.0 offered tty security ahead of its time---
- several months afterwards, Sun released a ``critical'' security patch
- with essentially the same security tests. Now pty 4.0 offers proven
- security. Although you can install and use the package without
- privileges, system administrators can install pty 4.0 so that it
- X_guarantees_ that nobody else has access to your tty. I'm offering a
- cash reward for anyone who can subvert these guarantees.
- X
- X* Session management - If you run your shell under pty, and the
- connection is hung up, you can log in again and reconnect. The session
- management model is extremely simple---it has just three primitives---
- yet powerful enough to accomplish tricky tasks, such as recording the
- output from a process after the process has started. A paper in the
- package, ``An introduction to session management,'' leads even novice
- users through competent use of session management commands.
- X
- X* Automatic installation - pty 4.0 comes with a completely automated
- configure/compile/install/verify-configuration setup. It will configure
- itself properly for most popular systems without human input.
- X
- X* Modularity - When I say that pty is meant as the sole interface to
- pseudo-terminals, I mean it! pty doesn't get in the way of direct,
- efficient pseudo-terminal I/O. So you can use it as a component of other
- programs which add input line editing, virtual screen support, or other
- fancy features. pty handles just one job, and handles it so cleanly that
- you'll never have to duplicate pseudo-terminal code in another program.
- X
- X* Free utilities - pty 4.0 comes with even more useful utilities than
- pty 3.0. It includes ten improved clones of standard utilities, notably
- a version of ``script'' which makes a proper utmp entry; and thirty new
- tools ranging from administrative helpers to ``tscript'', which records
- an interactive session _including the timing between characters_. Power
- users will appreciate ``nobuf'', which uses pty to transparently turn
- off stdio buffering in any program.
- X
- X* Free libraries - The pty package comes with several of my favorite
- libraries: env, fmt, getopt, radixsort, ralloc, scan, sigdfl, sigsched,
- sod, timer, username. You can use all of these for your own programs.
- X
- X* POSIX support - pty 4.0 works without trouble under popular POSIX/BSD
- systems, including Ultrix 4.1 and SunOS 4.1.1. All the job control
- features have been adapted to work with POSIX job control. pty should
- also be included with BSD 4.4.
- X
- X* Detailed documentation - Let your worries about incomplete program
- documentation be over. The pty package includes more than five thousand
- lines of documentation: forty quick-reference man pages; papers on
- controlling ttys, job control, session management, and user log files;
- extensive notes on pty internals and porting issues; and more.
- X
- Okay, enough hype. What's pty good for? Once upon a time nethack would,
- if you were lucky, produce characters with both a ring of polymorph and
- a ring of polymorph control. I wanted to run nethack inside a script
- which would keep rerolling characters until it saw that combination.
- X(Playing by the rules was never my forte.) Unfortunately, nethack didn't
- like having its input and output redirected. So I wrote the first
- versions of pty. ``pty nethack'' worked just like ``nethack'' but could
- be invoked in the middle of a pipe inside a script. As the years went
- by, pty became somewhat more powerful and flexible, but its basic
- function has always remained the same: to run programs, especially
- X``interactive'' programs, under a pseudo-tty. Despite this single-minded
- attitude, pty has wormed its way into the solutions to dozens of
- problems, ranging from buffer control to automating telnet scripts to
- making rlogind secure. Pseudo-terminal code seems to spring up
- everywhere; pty is your weapon to slash that code to a single line.
- XEnjoy!
- X
- X---Dan Bernstein, brnstnd@nyu.edu
- END_OF_FILE
- if test 4340 -ne `wc -c <'BLURB'`; then
- echo shar: \"'BLURB'\" unpacked with wrong size!
- fi
- # end of 'BLURB'
- fi
- if test -f 'INSTALL.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'INSTALL.c'\"
- else
- echo shar: Extracting \"'INSTALL.c'\" \(6231 characters\)
- sed "s/^X//" >'INSTALL.c' <<'END_OF_FILE'
- X#include <pwd.h>
- X#include <grp.h>
- X#include <stdio.h>
- X#include <sys/types.h>
- X#include <sys/file.h>
- X#include <sys/stat.h>
- X#include "config/ptybin.h"
- X#include "config/ptydir.h"
- X#include "config/ptygroup.h"
- X#include "config/sessconnfile.h"
- X#include "config/sessfile.h"
- X#include <utmp.h>
- X#include "config/utmpfile.h"
- X#include "config/wtmpfile.h"
- X#include "config/ptygroup.h"
- X
- char ptybin[] = PTYBIN;
- char ptydir[] = PTYDIR;
- char sessconnnow[] = SESSCONNNOW_FILE;
- char sessconnlog[] = SESSCONNLOG_FILE;
- char sessnow[] = SESSNOW_FILE;
- char sesslog[] = SESSLOG_FILE;
- char utmp[] = UTMP_FILE;
- char wtmp[] = WTMP_FILE;
- X
- static int num = 0;
- X
- void section(s)
- char *s;
- X{
- X ++num;
- X printf("\n%d. %s.\n",num,s);
- X}
- X
- int dontskip(s,t,u)
- char *s;
- char *t;
- char *u;
- X{
- X char buf[100];
- X char format[200];
- X sprintf(format,"! %s: ",s);
- X printf(format,t,u);
- X if (fgets(buf,sizeof(buf),stdin) == 0)
- X return 0;
- X if (buf[0] == 'o')
- X {
- X puts("Okay.");
- X return 1;
- X }
- X if (buf[0] == 's')
- X {
- X puts("Skipped.");
- X return 0;
- X }
- X return 1;
- X}
- X
- copyf2d(fn,dirfn)
- char *fn;
- char *dirfn;
- X{
- X int fdold;
- X int fdnew;
- X int r;
- X int n;
- X int w;
- X char buf[16384];
- X
- X fdold = open(fn,O_RDONLY);
- X if (fdold == -1)
- X return -1;
- X fdnew = open(dirfn,O_WRONLY | O_CREAT | O_TRUNC,0600);
- X if (fdnew == -1)
- X { close(fdold); return -1; }
- X while ((r = read(fdold,buf,sizeof(buf))) > 0)
- X {
- X n = 0;
- X while (n < r)
- X {
- X w = write(fdnew,buf + n,r - n);
- X if (w == -1)
- X {
- X close(fdold); close(fdnew); return -1;
- X }
- X n += w;
- X }
- X }
- X close(fdnew);
- X close(fdold);
- X if (r == -1)
- X return -1;
- X return 0;
- X}
- X
- static char *ptyuname = "pty";
- X
- chownpty(fn)
- char *fn;
- X{
- X struct passwd *own;
- X own = getpwnam(ptyuname);
- X if (!own)
- X return -1;
- X return chown(fn,own->pw_uid,-1);
- X}
- X
- chgrptty(fn)
- char *fn;
- X{
- X struct group *grp;
- X grp = getgrnam("tty");
- X if (!grp)
- X return -1;
- X return chown(fn,-1,grp->gr_gid);
- X}
- X
- touch(fn)
- char *fn;
- X{
- X int fd;
- X fd = open(fn,O_WRONLY | O_CREAT,0644);
- X if (fd == -1)
- X return -1;
- X close(fd);
- X if (chmod(fn,0644) == -1)
- X return -1;
- X if (chownpty(fn) == -1)
- X return -1;
- X return 0;
- X}
- X
- static char CHOPTYSS[100] = "chown pty %s/%s";
- X
- dobin(fn,level)
- char *fn;
- int level;
- X{
- X char dirfn[sizeof(ptybin) + 50];
- X sprintf(dirfn,"%s/%s",ptybin,fn);
- X if (dontskip("cp %s %s",fn,ptybin))
- X if (copyf2d(fn,dirfn) == -1)
- X perror("copy failed");
- X switch(level)
- X {
- X case 0:
- X if (dontskip("chmod 755 %s/%s",ptybin,fn))
- X if (chmod(dirfn,0755) == -1)
- X perror("chmod: cannot change mode");
- X break;
- X case 1:
- X if (dontskip("chmod 755 %s/%s",ptybin,fn))
- X if (chmod(dirfn,0755) == -1)
- X perror("chmod: cannot change mode");
- X break;
- X case 2:
- X if (dontskip("chgrp tty %s/%s",ptybin,fn))
- X if (chgrptty(dirfn) == -1)
- X perror("chgrp: cannot change group");
- X if (dontskip("chmod 2755 %s/%s",ptybin,fn))
- X if (chmod(dirfn,02755) == -1)
- X perror("chmod: cannot change mode");
- X break;
- X case 3:
- X if (dontskip(CHOPTYSS,ptybin,fn))
- X if (chownpty(dirfn) == -1)
- X perror("chown: cannot change owner");
- X if (dontskip("chmod 4755 %s/%s",ptybin,fn))
- X if (chmod(dirfn,04755) == -1)
- X perror("chmod: cannot change mode");
- X break;
- X }
- X}
- X
- main(argc,argv)
- int argc;
- char *argv[];
- X{
- X if (argv[1])
- X {
- X ptyuname = argv[1];
- X sprintf(CHOPTYSS,"chown %s %%s/%%s",ptyuname);
- X }
- X printf("I assume you've already set up a %s user and a tty (%d) group.\n\n",ptyuname,PTYGROUP);
- X printf("Each action will be printed before it is run. Press return to proceed.\n");
- X printf("Type skip (or anything beginning with an s) to skip a step.\n");
- X
- X section("Make pty session directory");
- X if (dontskip("mkdir %s",ptydir,""))
- X if (mkdir(ptydir,0700) == -1)
- X perror("mkdir: cannot create directory");
- X if (dontskip("chown %s %s",ptyuname,ptydir))
- X if (chownpty(ptydir) == -1)
- X perror("chown: cannot change owner");
- X
- X section("Make pty binary directory");
- X if (dontskip("mkdir %s",ptybin,""))
- X if (mkdir(ptybin,0700) == -1)
- X perror("mkdir: cannot create directory");
- X if (dontskip("chmod 755 %s",ptybin,""))
- X if (chmod(ptybin,0755) == -1)
- X perror("chmod: cannot change mode");
- X
- X section("Make session and session-connection log files");
- X if (dontskip("touch %s",sessnow,""))
- X if (touch(sessnow) == -1)
- X perror("touch: cannot touch file");
- X if (dontskip("touch %s",sesslog,""))
- X if (touch(sesslog) == -1)
- X perror("touch: cannot touch file");
- X if (dontskip("touch %s",sessconnnow,""))
- X if (touch(sessconnnow) == -1)
- X perror("touch: cannot touch file");
- X if (dontskip("touch %s",sessconnlog,""))
- X if (touch(sessconnlog) == -1)
- X perror("touch: cannot touch file");
- X
- X section("Make utmp and wtmp files (note: utmp will be owned by pty)");
- X if (dontskip("touch %s",utmp,""))
- X if (touch(utmp) == -1)
- X perror("touch: cannot touch file");
- X if (dontskip("touch %s",wtmp,""))
- X if (touch(wtmp) == -1)
- X perror("touch: cannot touch file");
- X
- X section("Copy executables into pty binary directory");
- X dobin("argv0",1);
- X dobin("biff",1);
- X dobin("checkptys",1);
- X dobin("condom",0);
- X dobin("ctrlv",1);
- X dobin("disconnect",3);
- X dobin("excloff",1);
- X dobin("exclon",1);
- X dobin("lock",1);
- X dobin("mesg",1);
- X dobin("nobuf",0);
- X dobin("pty",3);
- X dobin("reconnect",3);
- X dobin("script",0);
- X dobin("script.tidy",0);
- X dobin("sess",0);
- X dobin("sesskill",3);
- X dobin("sesslist",3);
- X dobin("sessmenu",1);
- X dobin("sessname",3);
- X dobin("sesswhere",1);
- X dobin("sesswho",1);
- X dobin("tiocsti",1);
- X dobin("tplay",1);
- X dobin("trecord",1);
- X dobin("tscript",0);
- X dobin("tty",1);
- X dobin("ttydetach",1);
- X dobin("ttyprotect",0);
- X dobin("users",1);
- X dobin("utmpinit",1);
- X dobin("waitfor",1);
- X dobin("wall",2);
- X dobin("who",1);
- X dobin("whoami",1);
- X dobin("write",2);
- X dobin("wtmprotate",0);
- X dobin("sessrotate",0);
- X dobin("sclogrotate",0);
- X dobin("sessnowinit",0);
- X dobin("scnowinit",0);
- X
- X section("Add log file rotations to daily, weekly, or monthly cron scripts");
- X printf("I'll leave this to you.\n");
- X printf("You may want to invoke wtmprotate, sessrotate, or sclogrotate.\n");
- X
- X section("Add utmp/sessnow/scnow initializations to /etc/rc.local");
- X printf("I'll leave this to you.\n");
- X printf("You may want to invoke utmpinit, sessnowinit, or scnowinit.\n");
- X
- X exit(0);
- X}
- END_OF_FILE
- if test 6231 -ne `wc -c <'INSTALL.c'`; then
- echo shar: \"'INSTALL.c'\" unpacked with wrong size!
- fi
- # end of 'INSTALL.c'
- fi
- if test -f 'NOTES' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'NOTES'\"
- else
- echo shar: Extracting \"'NOTES'\" \(5508 characters\)
- sed "s/^X//" >'NOTES' <<'END_OF_FILE'
- Random implementation notes:
- X
- Master now runs with uid set to euid. This ensures the integrity of
- various log files at the expense of proper CPU time accounting. On the
- other hand, telnetd and rlogind run as root, so who cares?
- X
- The rotate scripts kludge a bit to preserve the owner of each log file.
- Any better solutions?
- X
- Makefile uses cc -E and makes several assumptions about cpp for creating
- wtmprotate and friends.
- X
- Certain C programs had control charaters, but they're gone. script.tidy
- has control characters, but script.tidy.sh doesn't---Makefile fixes it.
- X
- Note that the assumption of two-byte tty extensions runs throughout the
- code.
- X
- It might be good to have the master refuse info requests while preco.
- X
- If the signaller can't change to PTYDIR, or if it can't read the
- extension over a pipe from the master, or if various other
- pseudo-possible conditions happen, the master may end up disconnected
- even if it's not a session. The child will not run until the first
- successful reconnect.
- X
- Note that the master switches controlling ttys only as a service to the
- slave. Nothing in the pty code depends on having the new tty as a ctty.
- This is a good thing, as BSD does not provide any reliable way to
- dissociate from the current controlling terminal. (Exercise: Show how
- TIOCEXCL may be used to prevent dissociation.)
- X
- Note that -R forces -T---a lot of things could go wrong otherwise.
- X
- Note that the slave unblocks various signals and sets various others to
- SIG_DFL; this will undo any blocking set up before pty was invoked.
- X
- Under -R, the master will ignore the tostop setting on the original tty.
- There's no good way to solve this---if you consider it a problem.
- X
- There'd be a race between fdsigler and fdsig2us if both master and
- sigler wrote and blocked at once. The solution is simple: fdsig2us is
- X*ONLY* for sigler-to-master communication. The signaller never waits for
- a reply on an fdsig2us message; instead it listens attentively for other
- commands. (Similarly, fdsigler is only for master-to-sigler
- communication, but only one way is necessary to avoid deadlock.)
- X
- Sun broke the behavior of getdtablesize(); I don't care.
- X
- X
- X
- command to change flagreading?
- X
- at last possible moment, check all ifdefs
- X
- X
- should ttydetach trash all tty descriptors?
- X
- should write isatty() and friends, use a cache of /dev stats
- also, isatty() should look at tty* before others. devname.c...
- verify cached device->name mapping by doing one stat()?
- X
- if system runs out of fds, won't be able to talk to master. is this a
- problem?
- X
- if a program like sesslist gets stopped or blocked, master won't be able
- to reconnect. is this a problem?
- X
- ptysuspend -sp7 - suspend as if child had stopped
- ptycont -sp7 ???
- pty -xtp7 ?????
- X
- should checkptys do a pff /dev/tty*?
- X
- last - clone
- tachywho
- X
- if child stops while master is disconnected, stop will be held for
- reconnecting signaller; child will be automatically continued on
- reconnect
- X
- should probably ignore SIGPIPE for utilities
- X
- note that an independent sesslist may temporarily show master as
- disconnected
- X
- definitely should not optimize sigsched
- X
- X
- X
- from ptypaper.9:
- X: Instead of copying pseudo-terminal modes from the original terminal, pty
- X: will set up a generic new-discipline line-at-a-time mode. (Footnote:
- X: This generic mode is defined in tty_initmodes() in the pty source. It
- X: should instead be described in further detail in some manual page.)
- now defined in ttydfl.7.
- X: Note that pty handles job control
- X: correctly
- it now handles it even better than correctly.
- X: Note that a
- X: session ``program'' is always restarted when it is reconnected.
- still true
- X: TERM signals
- no longer---master-sigler communication uses file descriptors
- X: Note that if it weren't for WINCH handling and the kernel support for
- X: controlling terminals, all mention of the original tty could disappear
- X: from the master. This would greatly simplify reconnect, though some
- X: stopping complexity would move into the signaller.
- this is now done---winch and cttys are both handled differently now
- X: Like all programs, this pty implementation has its caveats. Because the
- X: master needs a tty to reconnect to, the reconnecting signaller has to
- X: have a file descriptor open and pointing to its current tty.
- no longer true
- X: As documented, pseudo-ttys cannot handle EOF.
- doesn't really matter now given nobuf
- X: Raw-mode tty processing is problematic. There is no way to set a
- X: terminal's modes so that it does no processing and acts just like a
- X: pipe.
- ditto
- X: pty's random pseudo-tty searching should not be in a fixed order.
- it isn't any more
- X: None of the above (except TIOCEXCL, but there are still races) address
- X: the problem of a background process keeping access to a pseudo-tty.
- X: pty's test for previous access is completely reliable under some
- X: variants of the BSD kernel, and completely unreliable under others.
- new test is (i think) completely reliable under all variants
- X: A much better solution is to have utmp initialized at system startup to
- X: list all available ptys, in the order that login requires; then pty will
- X: conform to that order.
- now done, see utmpinit (thanks to paul graham)
- X: Even better would be to dedicate a file to
- X: session information: /etc/stmp, with permanent records in /usr/adm/ttmp.
- now done, sesslog/sessnow
- X: The association between connections and sessions is still logically
- X: separate and belongs in yet another file: say /etc/cstmp and
- X: /usr/adm/dttmp. pty would maintain these files along with stmp and ttmp.
- now done, sclog/scnow
- X
- END_OF_FILE
- if test 5508 -ne `wc -c <'NOTES'`; then
- echo shar: \"'NOTES'\" unpacked with wrong size!
- fi
- # end of 'NOTES'
- fi
- if test -f 'SECURITY' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'SECURITY'\"
- else
- echo shar: Extracting \"'SECURITY'\" \(5761 characters\)
- sed "s/^X//" >'SECURITY' <<'END_OF_FILE'
- X Mode 666 files are always dangerous---they let anyone read or
- X write anything, almost undetectably. ttys are particularly
- X dangerous because the data read from them controls practically
- X all user processes, and because the data written to them
- X controls these weird things called ``humans'' sitting on the
- X other side of the screen. ---me
- X
- An attacker can maintain access to a tty in several ways: (M) having the
- master side (say /dev/ptyp6) open; (S) having the slave side (that's
- X/dev/ttyp6) open; (T) having /dev/tty open and somehow referring to the
- tty; (C) having controlling tty p6. To see whether a pseudo-tty is
- secure, pty must test for each of these forms of access.
- X
- The system must provide several assurances. (1) To gain M, a process
- must open /dev/ptyp6 (or receive the descriptor from another process
- with M). (2) If any process has M then no process can open /dev/ptyp6.
- X(3) To gain S, a process must open /dev/ttyp6 (or receive the descriptor
- from another process with S), obeying any protections set up on
- X/dev/ttyp6. (4) To gain T, a process must open /dev/tty while it has C
- X(or receive the descriptor from another process with T). (5) To gain C,
- a process must already have M or S, or be forked from another process
- with C, or must open /dev/ttyp6. (6) If a process reads from M, the read
- will return 0 (or -1 with errno EIO) if and only if no process has S or
- T.
- X
- pty starts by opening /dev/ptyp6. It keeps /dev/ptyp6 open and never
- passes it to another process, so no other process will ever have M.
- pty then makes sure that /dev/ttyp6 is owned by its effective uid, and
- changes its mode to 0200. Unless it can do this, there is absolutely no
- chance of security, and pty won't even try the more powerful tests.
- X
- If the chmod succeeds, no unprivileged process will be able to open
- X/dev/ttyp6. pty then closes any descriptors it had open to /dev/ttyp6,
- and reads (in non-blocking mode) from /dev/ptyp6. It calls the pty
- insecure if the read returns anything but 0 or -1/EIO. Otherwise, it
- knows that at some point in time, no process had S, T, or M (except
- itself). It is an easy consequence of the facts stated so far that no
- process will ever have or acquire S or M until pty gives up M, changes
- the permissions on /dev/ttyp6, or otherwise gives away the show.
- X
- This level of security is where pty 3.0, expect, and Sun's patched
- versions of SunOS 4.1.1 telnetd/rlogind stop. The problem is that some
- processes could still have C and thus acquire T. How would you test
- whether there are any processes with a certain controlling tty? I've
- published two comprehensive solutions up to now: one based on
- eliminating C access entirely, one based on using my pff program to look
- for SCM access all at once. But there's a simpler (albeit somewhat less
- robust and somewhat slower) solution. pty 4.0 simply invokes /bin/ps
- cgaxtp6 and parses the output, looking for process IDs other than itself
- and that of ps. (It actually counts newlines in the output and error of
- ps. If there is some problem invoking ps, there will be zero newlines.
- If ps starts and prints any processes with pids other than that of pty
- and ps itself, there will be more than one newline. This is safe against
- strange process names and poor ps formatting, but it does require the
- pids of pty and ps to be positive integers which fit into an int.) On a
- Sun, for instance, this extra test takes 0.3 seconds on pty startup.
- Note that pty invokes /bin/ps with only the privileges of its real uid.
- X
- This isn't as robust as I'd like because a process could conceivably
- fork, exit in the parent, and have ps miss both processes without even
- printing an error message. If it works, though, it guarantees that at
- some point in time, no process (other than pty and ps) had S, M, or C
- access, and since no process can gain S or M, no process can gain C
- either.
- X
- We're not done yet. Some process might have picked up T access from C
- access, then shed C before ps detected it. (I have a test program which
- can do this about one time out of twenty; this isn't just a theoretical
- concern.) Fortunately, pty can just repeat its first test, detecting any
- S or T access. If read() returns 0 or -1/EIO, all S, T, C, and M access
- outside of pty is (I believe) completely gone. (pty then chmods and
- chowns the tty so that standard tools work.)
- X
- In retrospect, I don't really like this solution. It's actually rather
- portable (given BSD-flavor /bin/ps, that is), but it adds a noticeable
- delay at pty startup. I agree with Steve Bellovin that it makes much
- more sense to leave a pseudo-tty around (and accounted to the user!)
- until the user has closed all descriptors to it. This puts the delay in
- the background at the end of a pty session, rather than in the
- foreground at the beginning.
- X
- Life would have been much simpler if there had been a pseudotty() system
- call, say pseudotty(fd) int fd[4], which allocated a new tty (just like
- a pipe!) and returned master-read master-write slave-read slave-write
- descriptors. This would also have solved any problems pseudo-ttys have
- with EOF. Even without such a radical change, there's no reason for C or
- T access to exist; why can't the current tty be listed in an environment
- variable, like TTY=/dev/ttyp6? /dev/tty, controlling ttys, and POSIX
- sessions hurt security and make tty-manipulating programs harder to
- write. I can't say exactly what the UNIX philosophy is, but that sure
- ain't it.
- X
- Anyway, pty 4.0 works. It fits into current systems and closes every tty
- security hole I know. Now I'll put my money where my mouth is. I will
- pay $100.00 to the first person to demonstrate that pty 4.0's security
- can be defeated. To find out exactly what is required to claim this
- prize, send me e-mail.
- END_OF_FILE
- if test 5761 -ne `wc -c <'SECURITY'`; then
- echo shar: \"'SECURITY'\" unpacked with wrong size!
- fi
- # end of 'SECURITY'
- fi
- if test -f 'SYSCONF' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'SYSCONF'\"
- else
- echo shar: Extracting \"'SYSCONF'\" \(3977 characters\)
- sed "s/^X//" >'SYSCONF' <<'END_OF_FILE'
- X#!/bin/sh
- X# Public domain.
- X
- X# Configure, eat your heart out.
- X
- X# make sure we're using sh (stolen from Configure)
- PATH='.:/bin:/usr/bin:/usr/local/bin:/usr/ucb:/usr/local:/usr/lbin:/etc'
- export PATH || (echo "Aargh! This isn't sh. Desperation time. I will now feed myself to sh..."; exec sh $0; sh $0; kill $$)
- echo "We're using sh. Good."
- X
- X# make sure cmp works
- if cmp README README >/dev/null 2>&1
- then if cmp README SYSCONF >/dev/null 2>&1
- X then echo 'Aargh! cmp returns a zero exit code for different files. This is hopeless.'
- X exit 1
- X else echo 'You have a normal cmp. Good.'
- X fi
- else echo 'Aargh! cmp returns a nonzero exit code for the same file. This is hopeless.'
- X exit 1
- fi
- X
- X# config/posix.h: test for setsid()
- cat > .setsid.$$.c << 'YOW'
- extern setsid(); main() { setsid(); }
- YOW
- if cc -o .setsid.$$ .setsid.$$.c >/dev/null 2>&1
- then echo 'You have setsid(). I assume this is a POSIX system. Enabling -DPOSIX_SILLINESS...'
- X sed '4s/^#undef POSIX_SILLINESS$/#define POSIX_SILLINESS/' < config/posix.h > config/posix.h.$$
- X cp config/posix.h.$$ config/posix.h
- X rm config/posix.h.$$
- else echo 'This is not a POSIX system. Disabling -DPOSIX_SILLINESS...'
- X sed '4s/^#define POSIX_SILLINESS$/#undef POSIX_SILLINESS/' < config/posix.h > config/posix.h.$$
- X cp config/posix.h.$$ config/posix.h
- X rm config/posix.h.$$
- fi
- rm -f .setsid.$$ .setsid.$$.c
- X
- X# config/fdsettrouble.h: FD_ZERO
- cat > .fdzero.$$.c << 'YOW'
- X#include <sys/types.h>
- X#include <sys/time.h>
- main() { static fd_set rfds; FD_ZERO(&rfds); exit(0); }
- YOW
- if cc -o .fdzero.$$ .fdzero.$$.c >/dev/null 2>&1
- then echo 'You have FD_ZERO. Enabling it...'
- X sed '4s/^#define LACKING_FD_ZERO$/#undef LACKING_FD_ZERO/' < config/fdsettrouble.h > config/fdsettrouble.h.$$
- X cp config/fdsettrouble.h.$$ config/fdsettrouble.h
- X rm config/fdsettrouble.h.$$
- else echo 'You do not have FD_ZERO. Disabling it...'
- X sed '4s/^#undef LACKING_FD_ZERO$/#define LACKING_FD_ZERO/' < config/fdsettrouble.h > config/fdsettrouble.h.$$
- X cp config/fdsettrouble.h.$$ config/fdsettrouble.h
- X rm config/fdsettrouble.h.$$
- fi
- rm -f .fdzero.$$ .fdzero.$$.c
- X
- X# config/fdsettrouble.h: fd_set
- cat > .fdset.$$.c << 'YOW'
- X#include <sys/types.h>
- X#include <sys/time.h>
- main() { static fd_set rfds; exit(0); }
- YOW
- if cc -o .fdset.$$ .fdset.$$.c >/dev/null 2>&1
- then echo 'You have fd_set. Enabling it...'
- X sed '5s/^#define DESPERATE_FD_SET$/#undef DESPERATE_FD_SET/' < config/fdsettrouble.h > config/fdsettrouble.h.$$
- X cp config/fdsettrouble.h.$$ config/fdsettrouble.h
- X rm config/fdsettrouble.h.$$
- else echo 'You do not have fd_set. Disabling it...'
- X sed '5s/^#undef DESPERATE_FD_SET$/#define DESPERATE_FD_SET/' < config/fdsettrouble.h > config/fdsettrouble.h.$$
- X cp config/fdsettrouble.h.$$ config/fdsettrouble.h
- X rm config/fdsettrouble.h.$$
- fi
- rm -f .fdset.$$ .fdset.$$.c
- X
- X# config/ttyopts.h: TTY_WINDOWS
- cat > .winch.$$.c << 'YOW'
- X#include <signal.h>
- main() { int winch = SIGWINCH; exit(0); }
- YOW
- if cc -o .winch.$$ .winch.$$.c >/dev/null 2>&1
- then echo 'You have tty windows. Enabling them...'
- X sed '4s/^#undef TTY_WINDOWS$/#define TTY_WINDOWS/' < config/ttyopts.h > config/ttyopts.h.$$
- X cp config/ttyopts.h.$$ config/ttyopts.h
- X rm config/ttyopts.h.$$
- else echo 'You do not have tty windows. Disabling them... (I guess this is BSD 4.2.)'
- X sed '4s/^#define TTY_WINDOWS$/#undef TTY_WINDOWS/' < config/ttyopts.h > config/ttyopts.h.$$
- X cp config/ttyopts.h.$$ config/ttyopts.h
- X rm config/ttyopts.h.$$
- fi
- rm -f .winch.$$ .winch.$$.c
- X
- echo ' '
- echo 'Okay, now you should make CHECKCONF and run it.'
- echo 'If you would like me to do this for you, press return.'
- echo 'Otherwise type no and press return, or just interrupt this script.'
- read foo
- case x"$foo"y in
- xy) echo 'make CHECKCONF'
- X if make CHECKCONF
- X then echo './CHECKCONF'
- X ./CHECKCONF
- X else echo 'Oops, make CHECKCONF failed. I give up: you figure it out.'
- X fi
- X ;;
- esac
- X
- exit 0
- END_OF_FILE
- if test 3977 -ne `wc -c <'SYSCONF'`; then
- echo shar: \"'SYSCONF'\" unpacked with wrong size!
- fi
- chmod +x 'SYSCONF'
- # end of 'SYSCONF'
- fi
- if test -f 'TESTS' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'TESTS'\"
- else
- echo shar: Extracting \"'TESTS'\" \(5634 characters\)
- sed "s/^X//" >'TESTS' <<'END_OF_FILE'
- Typescript of a typical pty test run. EOF indicated by [^D]. Beeps
- indicated by ^G.
- X
- Script started on Fri Dec 20 01:17:45 PST 1991
- csh% who
- bernstei ttyp0 Dec 20 01:15 (annex2.Berkeley.)
- bernstei ttyq3 Dec 20 01:17 (script)
- csh% /bin/who
- bernstei ttyp0 Dec 20 01:15 (annex2.Berkeley.)
- bernstei ttyq3 Dec 20 01:17 (script)
- csh% users
- bernstei bernstei
- csh% /usr/ucb/users
- bernstei bernstei
- csh% tty
- X/dev/ttyq3
- csh% /bin/tty
- X/dev/ttyq3
- csh% who am i
- fred!bernstei ttyq3 Dec 20 01:17 (script)
- csh% /bin/who am i
- fred!bernstei ttyq3 Dec 20 01:17 (script)
- csh% who am i < /dev/tty
- csh% /bin/who am i < /dev/tty
- csh% who am i < /dev/null
- who: fatal: stdin not a tty
- csh% /bin/who am i < /dev/null
- bernstei tty?? Dec 20 01:18
- csh% whoami
- bernstei
- csh% /usr/ucb/whoami
- bernstei
- csh% echo hi > /dev/tty
- hi
- csh% echo hi > /dev/ttyq3
- hi
- csh% exclon
- csh% echo hi > /dev/tty
- X/dev/tty: Device busy
- csh% echo hi > /dev/ttyq3
- X/dev/ttyq3: Device busy
- csh% excloff
- csh% argv0 /usr/lib/sendmail mailq
- X Mail Queue (1 request)
- X--QID-- -Size- ----Q-Time----- ------------Sender/Recipient------------
- AA09963* (no control file)
- csh% lock
- Key:
- Again:
- Bad password!
- X^G^G^G^G^G^G^G^G^G^G^G^G^G^G^G^G^G^G^G^Gcsh% lock
- Key:
- Again:
- X^Gcsh% biff
- is n
- csh% mesg
- is y
- csh% pty pwd
- X/tmp/pty
- csh% pty who
- bernstei ttyp0 Dec 20 01:15 (annex2.Berkeley.)
- bernstei ttyq3 Dec 20 01:17 (script)
- csh% pty pty pty pty pty pwd
- X/tmp/pty
- csh% time pty pty pty pty pty pty pty pty pty pty pty pty pty pty pty pty pwd
- X/tmp/pty
- X0.0u 0.3s 0:21 1% 0+104k 0+0io 5pf+0w
- csh% pty -xu who
- bernstei ttyp0 Dec 20 01:15 (annex2.Berkeley.)
- bernstei ttyq3 Dec 20 01:17 (script)
- bernstei ttyr4 Dec 20 01:23 (pty4.0)
- csh% pty -xu pty -xu pty -xu who
- bernstei ttyp0 Dec 20 01:15 (annex2.Berkeley.)
- bernstei ttyq3 Dec 20 01:17 (script)
- bernstei ttyr7 Dec 20 01:23 (pty4.0)
- bernstei ttyr9 Dec 20 01:23 (pty4.0)
- bernstei ttyrb Dec 20 01:23 (pty4.0)
- csh% pty -xRu pty -xRu pty -xRu who
- bernstei ttyp0 Dec 20 01:15 (annex2.Berkeley.)
- bernstei ttyp2 Dec 20 01:23 (pty4.0)
- bernstei ttyp3 Dec 20 01:23 (pty4.0)
- bernstei ttyp4 Dec 20 01:23 (pty4.0)
- bernstei ttyq3 Dec 20 01:17 (script)
- csh% pty -xu -h random who
- bernstei ttyp0 Dec 20 01:15 (annex2.Berkeley.)
- bernstei ttyq3 Dec 20 01:17 (script)
- bernstei ttyrb Dec 20 01:24 (random)
- csh% tty
- X/dev/ttyq3
- csh% ttydetach sh
- X$ echo $$
- X2138
- X$ ps 2138
- X PID TT STAT TIME COMMAND
- X 2138 ? S 0:00 sh
- X$ [^D] csh% ttydetach pty -d pwd
- X/tmp/pty
- csh% pty -d pwd
- X/tmp/pty
- csh% tr '[A-Z]' '[a-z]' | cat -t
- one line of input
- another
- and yet another
- why no output?
- X[^D] one line of input
- another
- and yet another
- why no output?
- csh% nobuf tr '[A-Z]' '[a-z]' | cat -t
- one line of input
- one line of input
- ah, instant response!
- ah, instant response!
- the joy of buffer control!
- the joy of buffer control!
- X[^D] csh% sess sh
- X$ echo $PTY
- r7
- X$ sesslist
- session r7 pid 2174 slave 2175 connected
- X$ sesswho
- r7 Dec 20 01:29 bernstei 2174
- X$ sesswhere
- r7 Dec 20 01:29 connect (unknown)
- X$ disconnect
- csh% sesslist
- session r7 pid 2174 slave 2175 disconnected
- csh% sesswho
- r7 Dec 20 01:29 bernstei 2174
- csh% sess sh
- X$ sesslist
- session r7 pid 2174 slave 2175 disconnected
- session qf pid 2187 slave 2188 connected
- X$ sessname 'foo-prompt'
- X$ PS1=foo-prompt' '
- foo-prompt sesslist
- session r7 pid 2174 slave 2175 disconnected
- session qf pid 2187 slave 2188 connected: foo-prompt
- foo-prompt sessname
- session qf: foo-prompt
- foo-prompt reconnect r7
- reconnect: will connect to session r7 when session qf is done
- foo-prompt disconnect
- pty: info: reconnecting to r7
- pty: info: successfully connected to r7
- X$
- X$ reconnect qf
- reconnect: will connect to session qf when session r7 is done
- X$ disconnect
- pty: info: reconnecting to qf
- pty: info: successfully connected to qf
- foo-prompt reconnect r7
- reconnect: will connect to session r7 when session qf is done
- foo-prompt disconnect
- pty: info: reconnecting to r7
- pty: info: successfully connected to r7
- X$ reconnect qf
- reconnect: will connect to session qf when session r7 is done
- X$ sesslist
- session r7 pid 2174 slave 2175 connected (will drop into qf)
- session qf pid 2187 slave 2188 disconnected: foo-prompt
- X$ [^D] pty: info: reconnecting to qf
- pty: info: successfully connected to qf
- foo-prompt sesslist
- session qf pid 2187 slave 2188 connected: foo-prompt
- foo-prompt sesswhere
- qf Dec 20 01:32 connect (unknown)
- foo-prompt sesswho
- qf Dec 20 01:31 bernstei 2187
- foo-prompt sesslist
- session qf pid 2187 slave 2188 connected: foo-prompt
- foo-prompt sesskill -mysqf
- csh% set notify
- csh% pty sh
- X$ ^Z
- Stopped
- csh% bg
- X[1] pty sh &
- X
- X[1] + Stopped (tty output) pty sh
- csh% bg
- X[1] pty sh &
- X
- X[1] + Stopped (tty output) pty sh
- csh% bg
- X[1] pty sh &
- X
- X[1] + Stopped (tty output) pty sh
- csh% bg
- X[1] pty sh &
- csh% bg
- X[1] + Stopped (tty output) pty sh
- X
- X[1] pty sh &
- csh% bg
- bg: No current job.
- csh% bg
- bg: No current job.
- X
- X[1] + Stopped (tty output) pty sh
- csh% bg
- X[1] pty sh &
- csh%
- X[1] + Stopped (tty output) pty sh
- bg
- X[1] pty sh &
- csh%
- X[1] + Stopped (tty output) pty sh
- bg;bg;bg;
- X[1] pty sh &
- X
- X[1] + Stopped (tty output) pty sh
- X[1] pty sh &
- bg: No current job.
- csh%
- X[1] + Stopped (tty output) pty sh
- X
- csh% fg
- pty sh
- X
- X$
- X$ [^D] csh% pty sh &
- X[1] 2224
- csh%
- X[1] + Stopped (tty output) pty sh
- X
- csh% bg
- X[1] pty sh &
- X
- X[1] + Stopped (tty output) pty sh
- csh% bg
- X[1] pty sh &
- X
- X[1] + Stopped (tty output) pty sh
- csh% fg
- pty sh
- X$
- X$
- X$
- X$ [^D] csh% pty sh
- X$ echo $PTY
- r6
- X$ ttydetach sh
- X$ echo $PTY
- X
- X$ [^D] $ [^D] csh% jobs
- csh% [^D] Script done on Fri Dec 20 01:40:01 PST 1991
- END_OF_FILE
- if test 5634 -ne `wc -c <'TESTS'`; then
- echo shar: \"'TESTS'\" unpacked with wrong size!
- fi
- # end of 'TESTS'
- fi
- if test -f 'checkptys.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'checkptys.c'\"
- else
- echo shar: Extracting \"'checkptys.c'\" \(5995 characters\)
- sed "s/^X//" >'checkptys.c' <<'END_OF_FILE'
- X#include <stdio.h>
- X#include <sys/types.h>
- X#include <sys/stat.h>
- X#include <sys/file.h>
- X#include "config/devmty.h"
- X#include "config/devsty.h"
- X#include "config/posix.h" /* XXX: why? */
- X#include "config/ptybin.h"
- X#include "config/ptydir.h"
- X#include "config/ptyext.h"
- X#include "config/ptygroup.h"
- X#include "config/ptymodes.h"
- X#include "config/ptyopts.h"
- X#include "config/sessconnfile.h"
- X#include "config/sessfile.h"
- X#include <utmp.h>
- X#include "config/utmpfile.h"
- X#include "config/wtmpfile.h"
- X
- char ptybin[] = PTYBIN;
- char ptydir[] = PTYDIR;
- char sessconnnow[] = SESSCONNNOW_FILE;
- char sessconnlog[] = SESSCONNLOG_FILE;
- char sessnow[] = SESSNOW_FILE;
- char sesslog[] = SESSLOG_FILE;
- char utmp[] = UTMP_FILE;
- char wtmp[] = WTMP_FILE;
- X
- char devmty[sizeof(DEVMTY) + 5] = DEVMTY;
- char devsty[sizeof(DEVSTY) + 5] = DEVSTY;
- char pty1[] = PTYEXT1;
- char pty2[] = PTYEXT2;
- X
- void aack(s,t)
- char *s;
- char *t;
- X{
- X printf("aack! %s: %s\n",s,t);
- X}
- X
- void checkbin(s,level)
- char *s;
- int level; /* 0 script 1 normal 2 setgid 3 setuid */
- X{
- X struct stat st;
- X
- X if (stat(s,&st) == -1)
- X {
- X aack("executable does not exist",s);
- X return;
- X }
- X if (!(st.st_mode & 001))
- X aack("program isn't world-executable",s);
- X switch(st.st_mode & 06000)
- X {
- X case 0:
- X if (level == 2)
- X aack("program should be setgid but isn't",s);
- X if (level == 3)
- X aack("program should be setuid but isn't",s);
- X break;
- X case 06000:
- X case 04000:
- X if (level == 0)
- X aack("SHELL SCRIPT IS SETUID! FIX IMMEDIATELY!",s);
- X if (level == 1)
- X aack("program is SETUID, shouldn't be",s);
- X if (level == 2)
- X aack("program is SETUID, should be setgid instead",s);
- X break;
- X case 02000:
- X if (level == 0)
- X aack("SHELL SCRIPT IS SETGID! FIX IMMEDIATELY!",s);
- X if (level == 1)
- X aack("program is setgid, shouldn't be",s);
- X if (level == 3)
- X aack("program is setgid, should be setuid instead",s);
- X break;
- X default:
- X aack("computer doesn't understand binary arithmetic",s);
- X }
- X if (st.st_mode & 002)
- X aack("program is WORLD-writable",s);
- X if ((level == 2) && (st.st_gid != PTYGROUP))
- X aack("program should be setgid to tty group but isn't",s);
- X}
- X
- void testpath(s)
- char *s;
- X{
- X char *t;
- X struct stat st;
- X char old;
- X
- X if (*s != '/')
- X {
- X aack("path does not start with a slash",s);
- X return;
- X }
- X t = s + 1;
- X for (;;)
- X {
- X if ((*t == '/') || (*t == 0))
- X {
- X old = *t;
- X *t = 0;
- X if (stat(*s ? s : "/",&st) == -1)
- X aack("cannot stat component of path",*s ? s : "/");
- X else
- X {
- X if (st.st_mode & 022)
- X aack("component directory is WORLD- and group-writable",*s ? s : "/");
- X else if (st.st_mode & 002)
- X aack("component directory is WORLD-writable",*s ? s : "/");
- X else if (st.st_mode & 020)
- X aack("component directory is group-writable",*s ? s : "/");
- X }
- X *t = old;
- X }
- X if (!*t)
- X break;
- X ++t;
- X }
- X}
- X
- void testpty(fnm,fns,unusedptyowner)
- char *fnm;
- char *fns;
- int unusedptyowner;
- X{
- X struct stat stm;
- X struct stat sts;
- X int fdm;
- X
- X if (stat(fnm,&stm) == -1)
- X return;
- X if (stat(fns,&sts) == -1)
- X {
- X aack("pty master has no corresponding slave",fnm);
- X return;
- X }
- X
- X fdm = open(fnm,O_RDWR);
- X if (stm.st_mode & 0777 != 0666)
- X {
- X aack("pty master has weird mode",fnm);
- X }
- X if (fdm == -1)
- X {
- X printf("pty master %s in use, slave owned by uid %d\n",fnm,sts.st_uid);
- X if (sts.st_mode & 006)
- X aack("pty slave allows WORLD access",fns);
- X if (sts.st_gid != PTYGROUP)
- X aack("pty slave group does not match standard tty group",fns);
- X if (sts.st_mode & 020)
- X printf("pty slave %s messages on\n",fns);
- X if (sts.st_mode & 0100)
- X printf("pty slave %s biff on\n",fns);
- X }
- X else
- X {
- X if (sts.st_uid != unusedptyowner)
- X aack("unused pty slave not owned by standard unused tty owner",fns);
- X close(fdm);
- X }
- X}
- X
- main()
- X{
- X int p1;
- X int p2;
- X
- X printf("Testing main pty directory %s...\n",ptydir);
- X testpath(ptydir);
- X printf("Testing utmp file %s...\n",utmp);
- X testpath(utmp);
- X printf("Testing wtmp file %s...\n",wtmp);
- X testpath(wtmp);
- X printf("Testing session log file %s...\n",sesslog);
- X testpath(sesslog);
- X printf("Testing current session file %s...\n",sessnow);
- X testpath(sessnow);
- X printf("Testing session-connection log file %s...\n",sessconnlog);
- X testpath(sessconnlog);
- X printf("Testing current session-connection file %s...\n",sessconnnow);
- X testpath(sessconnnow);
- X printf("Testing pty binary directory %s...\n",ptybin);
- X testpath(ptybin);
- X
- X for (p1 = 0;pty1[p1];++p1)
- X for (p2 = 0;pty2[p2];++p2)
- X {
- X devmty[sizeof(DEVMTY) - 1] = pty1[p1];
- X devmty[sizeof(DEVMTY)] = pty2[p2];
- X devsty[sizeof(DEVSTY) - 1] = pty1[p1];
- X devsty[sizeof(DEVSTY)] = pty2[p2];
- X testpty(devmty,devsty,geteuid());
- X }
- X
- X printf("Checking for actual pty-related binaries in %s...\n",ptybin);
- X if (chdir(ptybin) == -1)
- X {
- X aack("cannot switch to pty binary directory",ptybin);
- X }
- X else
- X {
- X checkbin("argv0",1);
- X checkbin("biff",1);
- X checkbin("checkptys",1);
- X checkbin("condom",0);
- X checkbin("ctrlv",1);
- X checkbin("disconnect",3);
- X checkbin("excloff",1);
- X checkbin("exclon",1);
- X checkbin("lock",1);
- X checkbin("mesg",1);
- X checkbin("nobuf",0);
- X checkbin("pty",3);
- X checkbin("reconnect",3);
- X checkbin("script",0);
- X checkbin("script.tidy",0);
- X checkbin("sess",0);
- X checkbin("sesskill",3);
- X checkbin("sesslist",3);
- X checkbin("sessmenu",1);
- X checkbin("sessname",3);
- X checkbin("sesswhere",1);
- X checkbin("sesswho",1);
- X checkbin("tiocsti",1);
- X checkbin("tplay",1);
- X checkbin("trecord",1);
- X checkbin("tscript",0);
- X checkbin("tty",1);
- X checkbin("ttydetach",1);
- X checkbin("ttyprotect",0);
- X checkbin("users",1);
- X checkbin("utmpinit",1);
- X checkbin("waitfor",1);
- X checkbin("wall",2);
- X checkbin("who",1);
- X checkbin("whoami",1);
- X checkbin("write",2);
- X checkbin("wtmprotate",0);
- X checkbin("sessrotate",0);
- X checkbin("sclogrotate",0);
- X checkbin("sessnowinit",0);
- X checkbin("scnowinit",0);
- X }
- X
- X exit(0);
- X}
- END_OF_FILE
- if test 5995 -ne `wc -c <'checkptys.c'`; then
- echo shar: \"'checkptys.c'\" unpacked with wrong size!
- fi
- # end of 'checkptys.c'
- fi
- if test -f 'env.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'env.c'\"
- else
- echo shar: Extracting \"'env.c'\" \(4098 characters\)
- sed "s/^X//" >'env.c' <<'END_OF_FILE'
- X/* env.c, env.h: environ library
- Daniel J. Bernstein, brnstnd@nyu.edu.
- Depends on ralloc.h.
- Requires strlen, strncmp, environ.
- X8/3/91: Fixed off-by-one [sigh] in env_put2().
- X7/24/91: Added env_put2(). Recoded in terms of ralloc macros.
- X7/18/91: Cleanups. env 1.1, public domain.
- X7/10/91: Was ralloc()ing too little; didn't init env right. Tnx CW/EW/HB.
- X6/29/91: Added env_unsetlen(), made env_add use it so string can be const.
- X6/28/91: Baseline. env 1.0, public domain.
- No known patent problems.
- X
- Thanks to Christian Wettergren <d88-cwe@pdc.kth.se>, Erik Wallin
- X<d87-ewa@pdc.kth.se>, and Harald Barth <d88-hba@tds.kth.se> for bug
- fixes.
- X
- This was originally meant as a portable version of putenv(). It expanded
- to include unsetenv() and getenv(). Note that the routines always
- maintain environ properly, so execvp() and friends will pick up the new
- variables. I recommend that programs which do a lot of environment
- manipulation work with strings and keep their own hash table, then use
- these routines to manipulate environ before an execvp().
- X
- env_init() does optional initialization. It returns 0 on success, -1 on
- failure. Note that env_init(), env_put(), and env_unset() may all change
- environ.
- X
- env_put("FOO=BAR") adds FOO=BAR to the environment, destroying any
- previous value of FOO. It returns 0 on success, -1 on failure. Note that
- previous versions of env.c required env_put's argument to be writable;
- this problem has been removed.
- X
- env_put2("FOO","BAR") is just like env_put("FOO=BAR"), except of course
- that in the second case the new environment variable refers to the
- string passed to env_put, while in the first case it refers to
- internally malloc()ed memory.
- X
- env_unset("FOO") unsets any variable FOO. It returns 0 on success, -1 on
- failure. It will always succeed if env_init() has previously succeeded.
- X
- env_get("FOO") returns the value of the first variable FOO, or 0 if
- there is no such variable.
- X
- env_pick() returns any FOO=BAR in the environment, or 0 if the
- environment is empty. This can be used to implement the BSD printenv
- call, or to clear the environment.
- X*/
- X
- X#include "env.h"
- X#include "ralloc.h"
- X
- static int init = 0;
- static int numenv;
- static int allocenv;
- X
- extern char *env_get(s)
- char *s;
- X{
- X int i;
- X int slen;
- X char *envi;
- X
- X slen = strlen(s);
- X for (i = 0;envi = environ[i];++i)
- X if ((!strncmp(s,envi,slen)) && (envi[slen] == '='))
- X return envi + slen + 1;
- X return 0;
- X}
- X
- extern char *env_pick()
- X{
- X return environ[0]; /* environ[numenv-1] would make (pick-unset)^n easier */
- X}
- X
- static void env_unsetlen(s,slen)
- char *s;
- int slen;
- X{
- X int i;
- X for (i = 0;i < numenv;++i)
- X if ((!strncmp(s,environ[i],slen)) && (environ[i][slen] == '='))
- X {
- X if (i < --numenv)
- X environ[i] = environ[numenv];
- X environ[numenv] = 0;
- X }
- X}
- X
- extern int env_unset(s)
- char *s;
- X{
- X if (!init)
- X if (env_init())
- X return -1;
- X env_unsetlen(s,strlen(s));
- X return 0;
- X}
- X
- static int env_realloc()
- X{
- X char **envp;
- X
- X allocenv = numenv + 30;
- X envp = environ;
- X environ = RALLOC(char *,allocenv + 1);
- X if (!environ)
- X {
- X environ = envp;
- X allocenv = numenv;
- X return -1;
- X }
- X numenv = 0;
- X while (*envp)
- X {
- X environ[numenv] = *envp;
- X ++numenv;
- X ++envp;
- X }
- X environ[numenv] = 0;
- X RFREE(envp - numenv);
- X return 0;
- X}
- X
- static int env_add(s)
- char *s;
- X{
- X char *t;
- X for (t = s;*t;++t)
- X if (*t == '=')
- X {
- X env_unsetlen(s,t - s);
- X break;
- X }
- X if (numenv == allocenv)
- X if (env_realloc())
- X return -1;
- X environ[numenv] = s;
- X ++numenv;
- X environ[numenv] = 0;
- X return 0;
- X}
- X
- int env_init()
- X{
- X char **envp;
- X
- X numenv = 0;
- X allocenv = 0;
- X envp = environ;
- X environ = RALLOC(char *,1);
- X if (!environ)
- X {
- X environ = envp;
- X return -1;
- X }
- X environ[0] = 0;
- X init = 1;
- X while (*envp)
- X {
- X if (env_add(*envp))
- X return -1;
- X ++envp;
- X }
- X return 0;
- X}
- X
- int env_put(s)
- char *s;
- X{
- X if (!init)
- X if (env_init())
- X return -1;
- X return env_add(s);
- X}
- X
- int env_put2(s,t)
- char *s;
- char *t;
- X{
- X char *u;
- X int slen;
- X slen = strlen(s);
- X u = ralloc(slen + strlen(t) + 2);
- X if (!u)
- X return -1;
- X strcpy(u,s);
- X u[slen] = '=';
- X strcpy(u + slen + 1,t);
- X return env_put(u);
- X}
- END_OF_FILE
- if test 4098 -ne `wc -c <'env.c'`; then
- echo shar: \"'env.c'\" unpacked with wrong size!
- fi
- # end of 'env.c'
- fi
- if test -f 'fmt.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'fmt.c'\"
- else
- echo shar: Extracting \"'fmt.c'\" \(5780 characters\)
- sed "s/^X//" >'fmt.c' <<'END_OF_FILE'
- X/* fmt.c, fmt.h: formatting library
- Daniel J. Bernstein, brnstnd@nyu.edu.
- No dependencies.
- No environment requirements.
- X10/5/91: Cleaned up fmt_rvis, added fmt_unrvis.
- X9/1/91: Added fmt_nvis, fmt_rvis.
- X8/28/91: Added fmt_vis.
- X7/18/91: Baseline. fmt 1.0, public domain.
- No known patent problems.
- X
- XXXX: still need floating-point formatting
- X
- X*/
- X
- X#include "fmt.h"
- X
- X/* To find out the actual length of the formatted value, pass a first
- argument of (char *) 0. */
- X
- X#define zero '0'
- X#define alow 'a'
- X#define plus '+'
- X#define minus '-'
- X#define xlow 'x'
- X
- unsigned int fmt_ulong(s,u) char *s; unsigned long u;
- X{
- X unsigned int len; unsigned long q;
- X len = 1; q = u;
- X while (q > 9) { ++len; q /= 10; }
- X if (s)
- X {
- X s += len;
- X do { *--s = zero + (u % 10); u /= 10; } while(u); /* handles u == 0 */
- X }
- X return len;
- X}
- X
- unsigned int fmt_xlong(s,u) char *s; unsigned long u;
- X{
- X unsigned int len; unsigned long q; unsigned long c;
- X len = 1; q = u;
- X while (q > 15) { ++len; q /= 16; }
- X if (s)
- X {
- X s += len;
- X do { c = u & 15; *--s = (c > 9 ? alow - 10 : zero) + c; u /= 16; } while(u);
- X }
- X return len;
- X}
- X
- unsigned int fmt_nbblong(s,n,base,bext,u)
- char *s; unsigned int n; unsigned int base; unsigned int bext; unsigned long u;
- X/* I hope this meaning of n (min, not max) doesn't hurt anything. */
- X{
- X unsigned int len; unsigned long q; unsigned long c;
- X len = 1; q = u; bext += base;
- X while (q > bext - 1) { ++len; q /= bext; } if (len < n) len = n;
- X if (s)
- X {
- X s += len;
- X do { c = u % bext; *--s = (c >= base ? alow - base : zero) + c; u /= bext; }
- X while(u);
- X }
- X return len;
- X}
- X
- unsigned int fmt_ushort(s,u) char *s; unsigned short u;
- X{
- X unsigned long l; l = u; return fmt_ulong(s,l);
- X}
- X
- unsigned int fmt_xshort(s,u) char *s; unsigned short u;
- X{
- X unsigned long l; l = u; return fmt_xlong(s,l);
- X}
- X
- unsigned int fmt_nbbshort(s,n,base,bext,u)
- char *s; unsigned int n; unsigned int base; unsigned int bext; unsigned short u;
- X{
- X unsigned long l; l = u; return fmt_nbblong(s,n,base,bext,l);
- X}
- X
- unsigned int fmt_uint(s,u) char *s; unsigned int u;
- X{
- X unsigned long l; l = u; return fmt_ulong(s,l);
- X}
- X
- unsigned int fmt_xint(s,u) char *s; unsigned int u;
- X{
- X unsigned long l; l = u; return fmt_xlong(s,l);
- X}
- X
- unsigned int fmt_nbbint(s,n,base,bext,u)
- char *s; unsigned int n; unsigned int base; unsigned int bext; unsigned int u;
- X{
- X unsigned long l; l = u; return fmt_nbblong(s,n,base,bext,l);
- X}
- X
- unsigned int fmt_plusminus(s,sign) char *s; int sign;
- X{
- X if (s) *s = ((sign < 0) ? minus : plus); return 1;
- X}
- X
- unsigned int fmt_minus(s,sign) char *s; int sign;
- X{
- X if (sign > 0) return 0;
- X if (s) *s = minus; return 1;
- X}
- X
- unsigned int fmt_0x(s,base) char *s; int base;
- X{
- X if (base == 10) return 0;
- X if (s) *s = zero; if (base == 8) return 1;
- X if (s) s[1] = xlow; return 2;
- X}
- X
- unsigned int fmt_strncpy(s,t,n) char *s; char *t; unsigned int n;
- X{
- X unsigned int len;
- X len = 0;
- X if (s) { while (s[len] = t[len]) if (++len == n) break; }
- X else { while (t[len]) if (++len == n) break; }
- X return len;
- X}
- X
- unsigned int fmt_memcpy(s,t,n) char *s; char *t; unsigned int n;
- X/* This and fmt_vis are the only functions where n == 0 means do nothing. */
- X{
- X unsigned int len;
- X if (s)
- X for (len = 0;len < n;++len)
- X s[len] = t[len];
- X return n;
- X}
- X
- static unsigned int fmt_xvis(s,t,n,x) char *s; char *t; unsigned int n; int x;
- X{
- X unsigned int len;
- X for (len = 0;n;--n,++t)
- X {
- X int ch;
- X ch = (int) (unsigned int) (unsigned char) *t;
- X /* XXX: ASCII dependent! */
- X if (ch > 127)
- X { if (s) { s[len] = 'M'; s[len + 1] = '-'; } len += 2; ch -= 128; }
- X if (((ch >= 32) && (ch <= 126)) || (ch == x))
- X { if (s) s[len] = ch; ++len; continue; }
- X if (s) s[len] = '^'; ++len;
- X if (s) s[len] = 64 + (ch & 31) - 32 * (ch == 127); ++len;
- X }
- X return len;
- X}
- X
- unsigned int fmt_vis(s,t,n) char *s; char *t; unsigned int n;
- X{
- X return fmt_xvis(s,t,n,-1);
- X}
- X
- unsigned int fmt_nvis(s,t,n) char *s; char *t; unsigned int n;
- X{
- X return fmt_xvis(s,t,n,'\n');
- X}
- X
- X/* invertible! */
- unsigned int fmt_rvis(s,t,n) char *s; char *t; unsigned int n;
- X{
- X unsigned int len;
- X for (len = 0;n;--n,++t)
- X {
- X int ch;
- X ch = (int) (unsigned int) (unsigned char) *t;
- X /* XXX: ASCII dependent! */
- X if ((ch >= 32) && (ch <= 126) && (ch != '^'))
- X { if (s) s[len] = ch; ++len; continue; }
- X if (ch == 127)
- X { if (s) { s[len] = '^'; s[len + 1] = '?'; } len += 2; continue; }
- X if (ch == '^')
- X { if (s) { s[len] = '^'; s[len + 1] = ' '; } len += 2; continue; }
- X if (ch == 10)
- X { if (s) { s[len] = '^'; s[len + 1] = '$'; } len += 2; continue; }
- X if ((ch >= 0) && (ch <= 31))
- X { if (s) { s[len] = '^'; s[len + 1] = 64 + (ch & 31); } len += 2; continue; }
- X if (s) { s[len] = '^'; s[len + 1] = 'x'; } len += 2;
- X if (s) { s[len] = (ch >= 160) ? alow + ((ch/16) - 10) : zero + (ch/16); }
- X ++len; ch = ch & 15;
- X if (s) { s[len] = (ch >= 10) ? alow + (ch - 10) : zero + ch; }
- X ++len;
- X }
- X s[len] = '\n';
- X return len + 1;
- X}
- X
- unsigned int fmt_unrvis(s,t,n) char *s; char *t; unsigned int n;
- X{
- X unsigned int len;
- X for (len = 0;n;--n,++t)
- X {
- X if (*t == '\n')
- X continue;
- X if (*t != '^')
- X {
- X if (s) *s++ = *t; ++len;
- X continue;
- X }
- X if (n < 2)
- X return len;
- X ++t; --n;
- X if (*t == '?')
- X { if (s) *s++ = 127; ++len; continue; }
- X if (*t == ' ')
- X { if (s) *s++ = '^'; ++len; continue; }
- X if ((*t >= 64) && (*t <= 95))
- X { if (s) *s++ = *t - 64; ++len; continue; }
- X if (*t == '$')
- X { if (s) *s++ = 10; ++len; continue; }
- X if (n < 3)
- X return len;
- X if (*t != 'x')
- X return len; /* XXX */
- X ++t;
- X if (s)
- X if (*t < alow)
- X *s = *t - zero;
- X else
- X *s = *t - alow + 10;
- X if (s)
- X *s <<= 4;
- X ++t;
- X if (s)
- X if (*t < alow)
- X *s += *t - zero;
- X else
- X *s += *t - alow + 10;
- X if (s) ++s;
- X ++len; n -= 2;
- X }
- X return len;
- X}
- END_OF_FILE
- if test 5780 -ne `wc -c <'fmt.c'`; then
- echo shar: \"'fmt.c'\" unpacked with wrong size!
- fi
- # end of 'fmt.c'
- fi
- if test -f 'scan.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'scan.c'\"
- else
- echo shar: Extracting \"'scan.c'\" \(5077 characters\)
- sed "s/^X//" >'scan.c' <<'END_OF_FILE'
- X/* scan.c, scan.h: scanning library
- Daniel J. Bernstein, brnstnd@nyu.edu.
- No dependencies.
- No environment requirements.
- X7/18/91: Baseline. scan 1.0, public domain.
- No known patent problems.
- X
- XXXX: still need floating-point scanning
- X
- X*/
- X
- X#include "scan.h"
- X
- X/* just to keep track of what special characters we're using */
- X#define zero '0'
- X#define plus '+'
- X#define minus '-'
- X#define alow 'a'
- X#define acap 'A'
- X#define space ' '
- X#define tab '\t'
- X#define xlow 'x'
- X#define xcap 'x'
- X
- X/* Note that the digits here are defined as '0', '0' + 1, '0' + 2, etc. */
- X/* The letters are defined similarly, starting from 'a' and 'A'. */
- X/* This may produce unintuitive results with a weird character set. */
- X
- unsigned int scan_plusminus(s,sign) char *s; int *sign;
- X{
- X if (*s == plus) { *sign = 1; return 1; }
- X if (*s == minus) { *sign = -1; return 1; }
- X *sign = 1; return 0;
- X}
- X
- unsigned int scan_0x(s,base) char *s; unsigned int *base;
- X{
- X if (*s == zero)
- X {
- X if ((s[1] == xlow) || (s[1] == xcap))
- X { *base = 16; return 2; }
- X *base = 8; return 1;
- X }
- X *base = 10; return 0;
- X}
- X
- unsigned int scan_ulong(s,u) char *s; unsigned long *u;
- X{
- X unsigned int pos; unsigned long result; unsigned long c;
- X pos = 0; result = 0;
- X while ((c = (unsigned long) (unsigned char) (s[pos] - zero)) < 10)
- X { result = result * 10 + c; ++pos; }
- X *u = result; return pos;
- X}
- X
- unsigned int scan_xlong(s,u) char *s; unsigned long *u;
- X{
- X unsigned int pos; unsigned long result; unsigned long c;
- X pos = 0; result = 0;
- X while (((c = (unsigned long) (unsigned char) (s[pos] - zero)) < 10)
- X ||(((c = (unsigned long) (unsigned char) (s[pos] - alow)) < 6)
- X &&(c = c + 10))
- X ||(((c = (unsigned long) (unsigned char) (s[pos] - acap)) < 6)
- X &&(c = c + 10))
- X ) /* XXX: this gets the job done */
- X { result = result * 16 + c; ++pos; }
- X *u = result; return pos;
- X}
- X
- unsigned int scan_nbblong(s,n,base,bext,u)
- char *s; unsigned int n; unsigned int base; unsigned int bext; unsigned long *u;
- X/* Note that n == 0 means scan forever. Hopefully this is a good choice. */
- X{
- X unsigned int pos; unsigned long result; unsigned long c;
- X pos = 0; result = 0;
- X while (((c = (unsigned long) (unsigned char) (s[pos] - zero)) < base)
- X ||(((c = (unsigned long) (unsigned char) (s[pos] - alow)) < bext)
- X &&(c = c + base))
- X ||(((c = (unsigned long) (unsigned char) (s[pos] - acap)) < bext)
- X &&(c = c + base))
- X ) /* XXX: this gets the job done */
- X { result = result * (base + bext) + c; ++pos; if (pos == n) break; }
- X *u = result; return pos;
- X}
- X
- unsigned int scan_uint(s,u) char *s; unsigned int *u;
- X{
- X unsigned int pos; unsigned long result;
- X pos = scan_ulong(s,&result);
- X *u = result; return pos;
- X}
- X
- unsigned int scan_xint(s,u) char *s; unsigned int *u;
- X{
- X unsigned int pos; unsigned long result;
- X pos = scan_xlong(s,&result);
- X *u = result; return pos;
- X}
- X
- unsigned int scan_nbbint(s,n,base,bext,u)
- char *s; unsigned int n; unsigned int base; unsigned int bext; unsigned int *u;
- X{
- X unsigned int pos; unsigned long result;
- X pos = scan_nbblong(s,n,base,bext,&result);
- X *u = result; return pos;
- X}
- X
- unsigned int scan_ushort(s,u) char *s; unsigned short *u;
- X{
- X unsigned int pos; unsigned long result;
- X pos = scan_ulong(s,&result);
- X *u = result; return pos;
- X}
- X
- unsigned int scan_xshort(s,u) char *s; unsigned short *u;
- X{
- X unsigned int pos; unsigned long result;
- X pos = scan_xlong(s,&result);
- X *u = result; return pos;
- X}
- X
- unsigned int scan_nbbshort(s,n,base,bext,u)
- char *s; unsigned int n; unsigned int base; unsigned int bext; unsigned short *u;
- X{
- X unsigned int pos; unsigned long result;
- X pos = scan_nbblong(s,n,base,bext,&result);
- X *u = result; return pos;
- X}
- X
- unsigned int scan_charsetnskip(s,chars,n) char *s; char *chars; unsigned int n;
- X{
- X unsigned int pos;
- X pos = 0;
- X while (chars[s[pos]]) /* user's responsibility to check for null */
- X if (++pos == n)
- X break;
- X return pos;
- X}
- X
- unsigned int scan_noncharsetnskip(s,chars,n) char *s; char *chars; unsigned int n;
- X{
- X unsigned int pos;
- X pos = 0;
- X while (!chars[s[pos]]) /* again, user's responsibility to check for null */
- X if (++pos == n)
- X break;
- X return pos;
- X}
- X
- unsigned int scan_whitenskip(s,n) char *s; unsigned int n;
- X{
- X unsigned int pos; char c;
- X pos = 0;
- X while (((c = s[pos]) == space) || (c == tab)) /* XXX: this is slow */
- X if (++pos == n)
- X break;
- X return pos;
- X}
- X
- unsigned int scan_nonwhitenskip(s,n) char *s; unsigned int n;
- X{
- X unsigned int pos; char c;
- X pos = 0;
- X /* This is the only function without ``str'' in its name where we
- X check specially for nulls. */
- X while ((c = s[pos]) && (c != space) && (c != tab)) /* XXX: this is slow */
- X if (++pos == n)
- X break;
- X return pos;
- X}
- X
- unsigned int scan_strncmp(s,t,n) char *s; char *t; unsigned int n;
- X{
- X unsigned int pos; char c;
- X pos = 0;
- X while ((c = s[pos]) && (c == t[pos]))
- X if (++pos == n)
- X break;
- X return pos;
- X}
- X
- unsigned int scan_memcmp(s,t,n) char *s; char *t; unsigned int n;
- X/* This is the only function where n == 0 means do nothing. */
- X{
- X unsigned int pos;
- X pos = 0;
- X while (n) if (s[pos] != t[pos]) break; else { --n; ++pos; }
- X return pos;
- X}
- END_OF_FILE
- if test 5077 -ne `wc -c <'scan.c'`; then
- echo shar: \"'scan.c'\" unpacked with wrong size!
- fi
- # end of 'scan.c'
- fi
- echo shar: End of archive 4 \(of 9\).
- cp /dev/null ark4isdone
- MISSING=""
- for I in 1 2 3 4 5 6 7 8 9 ; do
- if test ! -f ark${I}isdone ; then
- MISSING="${MISSING} ${I}"
- fi
- done
- if test "${MISSING}" = "" ; then
- echo You have unpacked all 9 archives.
- rm -f ark[1-9]isdone ark[1-9][0-9]isdone
- else
- echo You still need to unpack the following archives:
- echo " " ${MISSING}
- fi
- ## End of shell archive.
- exit 0
-